package com.ship.dispatch.modifiable;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.ship.common.security.utils.SecurityUtils;
import com.ship.dispatch.annotation.DataChangeLog;
import com.ship.dispatch.annotation.ModifiableField;
import com.ship.dispatch.bean.fob.Record;
import com.ship.dispatch.bpm.service.TaskService;
import com.ship.dispatch.mapper.fob.RecordMapper;
import com.ship.dispatch.service.fob.ModulContentParser;
import com.ship.dispatch.service.fob.RecordService;
import com.ship.dispatch.util.ReflectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 *@title DataChangeLogAspect
 *@description
 *@author yl
 *@create 2023/12/13 0013
 */
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class DataChangeLogAspect {
    @Autowired
    RecordService recordService;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private TaskService taskService;

    //保存修改之前的数据
    Map<String, Object> oldMap = new HashMap<>();

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(operateLog)")
    public void boBefore(JoinPoint joinPoint, DataChangeLog operateLog)
    {
        ModulContentParser contentParser = (ModulContentParser) applicationContext.getBean(operateLog.parseclass());
        Object oldObject = contentParser.getResult(joinPoint, operateLog);
        if(oldObject!=null){
            oldMap = (Map<String, Object>) objectToMap(oldObject); // 存储修改前的对象
        }
    }


    /**
     * 处理请求后执行
     * @param joinPoint
     * @param operateLog
     * @throws Throwable
     */
    @After("@annotation(operateLog)")
    public void around(JoinPoint joinPoint, DataChangeLog operateLog) throws Throwable {
        try {

            //ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            //HttpServletRequest request = attributes.getRequest();
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod(); // 获取切入点所在的方法
            String meName = method.getName(); // 获取请求的方法名
            String className = joinPoint.getTarget().getClass().getName(); // 获取请求的类名
            String methodName = className + "." + meName;
            //String uri = request.getRequestURL().toString(); // 请求uri

            Object[] args = joinPoint.getArgs();

            Object info = joinPoint.getArgs()[1];
            Object schemeDetailId = ReflectionUtils.getFieldValue(info, "schemeDetailId");
            Object key = ReflectionUtils.getFieldValue(info, "key");
            Object operationType = ReflectionUtils.getFieldValue(info, "operationType");
            Object processInstanceId = ReflectionUtils.getFieldValue(info, "processInstanceId");
            // 请求参数 预留
            //String requestParams = JSON.toJSONString(args);

            Object object = null;
            ModulContentParser contentParser;
            try {
                contentParser = (ModulContentParser) applicationContext.getBean(operateLog.parseclass());
                object = contentParser.getResult(joinPoint, operateLog);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("service加载失败:", e);
            }
            if ("add".equals(operateLog.type())) {
                Map<String, Object> dataMap = (Map<String, Object>) objectToMap(object);
                log.info("新增的数据:{}" + dataMap.toString());
            }
            else {
                // 生成变更批次id
                String bacthId = UUID.randomUUID().toString().trim().replaceAll("-", "");
                //比较新数据与数据库原数据
                List<Map<String, Object>> list = defaultDealUpdate(object, oldMap);
                String taskName = taskService.getNodeNameByKey(key+"");

                for (Map<String, Object> dataMap : list) {
                    Record changeRecord = new Record();
                    changeRecord.setBacthId(bacthId);
                    changeRecord.setSchemeDetailId(Long.parseLong(schemeDetailId.toString()));
                    changeRecord.setNodeKey(key.toString());
                    //创建人信息
                    changeRecord.setCreateDate(new Date());
                    //TODO 获取登录人信息
                    changeRecord.setProceeInstanceId(processInstanceId.toString());
                    changeRecord.setCreateBy(String.valueOf(SecurityUtils.getUsername()));
                    changeRecord.setFieldName(String.valueOf(dataMap.get("filedName")));
                    changeRecord.setOldValue(String.valueOf(dataMap.get("oldValue")));
                    changeRecord.setNewValue(String.valueOf(dataMap.get("newValue")));
                    changeRecord.setServiceId(String.valueOf(dataMap.get("id")));
                    changeRecord.setType(operateLog.type());
                    if("".equals(dataMap.get("oldValue"))){
                        String remark = "新填报 "+taskName+" "+changeRecord.getFieldName() + "为" + changeRecord.getNewValue();
                        changeRecord.setRemark(remark);
                    }else {
                        String remark = "修改"+ taskName +" " + changeRecord.getFieldName() + "为" + changeRecord.getNewValue() +
                                ",原" + changeRecord.getFieldName() + "为" +
                                (StringUtils.isBlank(changeRecord.getOldValue()) ? "空" : changeRecord.getOldValue());
                        changeRecord.setRemark(remark);
                    }
                    changeRecord.setCreateBy(String.valueOf(SecurityUtils.getUsername()));
                    recordService.save(changeRecord);
                }
                if("1".equals(operationType+"")){
                    Record changeRecord = new Record();
                    changeRecord.setBacthId("end");
                    changeRecord.setSchemeDetailId(Long.parseLong(schemeDetailId.toString()));
                    changeRecord.setNodeKey(key.toString());
                    //创建人信息
                    changeRecord.setCreateDate(new Date());
                    changeRecord.setRemark("完成 "+taskName +" 任务处理");
                    changeRecord.setProceeInstanceId(processInstanceId.toString());
                    //TODO 获取登录人信息
                    changeRecord.setCreateBy(String.valueOf(SecurityUtils.getUsername()));
                    recordService.save(changeRecord);
                }
            }
        } catch (Exception e) {
            log.info("自定义变更记录注解出现异常");
            e.printStackTrace();
        }

    }

    private List<Map<String, Object>> defaultDealUpdate(Object newObject, Map<String, Object> oldMap) {
        try {
            List<Map<String, Object>> list = new ArrayList<>();
            Map<String, Object> newMap = (Map<String, Object>) objectToMap(newObject);
            Object finalNewObject = newObject;
            if(newMap==null){
                return list;
            }
            oldMap.forEach((k, v) -> {
                Object newResult = newMap.get(k);
                if ((v==null && newResult!=null) || (null != v && !v.equals(newResult))) {
                    Field field = ReflectionUtils.getAccessibleField(finalNewObject, k);
                    ModifiableField dataName = field.getAnnotation(ModifiableField.class);
                    if (null != dataName && StringUtils.isNotEmpty(dataName.name())) {
                        Map result = new HashMap();
                        result.put("filedName", dataName.name());
                        result.put("id", newMap.get("id"));
                        result.put("oldValue", v==null?"":v);
                        result.put("newValue", newResult);
                        list.add(result);
                    }
                }
            });
            log.info("比较的数据哈:{}" + list.toString());
            return list;
        } catch (Exception e) {
            log.error("比较异常", e);
            e.printStackTrace();
            throw new RuntimeException("比较异常", e);
        }
    }

    private Map<?, ?> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 如果使用JPA请自己打开这条配置
        // mapper.addMixIn(Object.class, IgnoreHibernatePropertiesInJackson.class);
        Map<?, ?> mappedObject = mapper.convertValue(obj, Map.class);
        return mappedObject;
    }

    /**
     * 翻译
     *
     * @param propertyValue 参数值如：0
     * @param converterExp  翻译注解的值如：0=男,1=女,2=未知
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
}
